﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EnvDTE;
using EnvDTE80;
using DDW;
using DDW.Collections;
using System.IO;
using DDW.Names;
using System.Reflection;
using SDILReader;

namespace SD007
{
    public class MethodAbstraction
    {
        private CodeFunction2 methodObject;        
        private List<VariableAbstraction> classInstanceVariables = new List<VariableAbstraction>();
        //private List<String> instanceVariablesAccessed = new List<String>();
        private List<VariableAbstraction> localVariables;       

        public List<VariableAbstraction> LocalVariables
        {
            get 
            {
                if(localVariables == null)
                    this.localVariables = this.Visitor.localVariables;
                return localVariables; 
            }
        }
        
        private List<VariableAbstraction> parameters = new List<VariableAbstraction>();

        public List<VariableAbstraction> instanceVariablesAccessed
        {
            get
            {
                List<VariableAbstraction> retorno;
                List<VariableAbstraction> variablesAccessed = this.Visitor.variablesAccessed;
                List<VariableAbstraction> instanceVarAccessed =
                    this.classInstanceVariables.Where(v => variablesAccessed.Where(va => va.Name == v.Name).Count() > 0).ToList();
                List<VariableAbstraction> localVarAccessed =
                    this.Visitor.localVariables.Where(l => variablesAccessed.Where(va => va.Name == l.Name).Count() > 0).ToList();
                List<VariableAbstraction> paramsAccessed =
                    this.parameters.Where(p => variablesAccessed.Where(va => va.Name == p.Name).Count() > 0).ToList();
                retorno = instanceVarAccessed.Where(i => localVarAccessed.Where(l => l.Name == i.Name).Count() == 0).ToList();
                retorno = retorno.Where(r => paramsAccessed.Where(p => p.Name == r.Name).Count() == 0).ToList();
                return retorno;
            }
        }

        private SD007Visitor visitor;
        private SD007Visitor Visitor
        {
            get
            {
                if (visitor == null)
                {
                    visitor = new SD007Visitor();
                    ParseMethodBody();
                }
                return visitor;
            }
        }


        int? cyclo = null;

        public int CYCLO
        {
            get
            {
                cyclo = cyclo ?? CalculateCYCLO();
                return cyclo.GetValueOrDefault(0);
            }
        }

        int? nop = null;

        public int NOP
        {
            get
            {
                nop = nop ?? CalculateNOP();
                return nop.GetValueOrDefault(0);
            }
        }

        int? loc = null;

        public int LOC
        {
            get
            {
                loc = loc ?? CalculateLOC();
                return loc.GetValueOrDefault(0);
            }
        }

        int? noav = null;

        public int NOAV
        {
            get
            {
                noav = noav ?? CalculateNOAV();
                return noav.GetValueOrDefault(0);
            }
        }

        int? maxnesting = null;

        public int MAXNESTING
        {
            get
            {
                maxnesting = maxnesting ?? CalculateMAXNESTING();
                return maxnesting.GetValueOrDefault(0);
            }
        }

        public CodeFunction2 MethodObject
        {
            get
            {
                return this.methodObject;
            }
        }

        private CodeClass2 contaningClass;

        public CodeClass2 ContainingClass
        {
            get { return contaningClass; }
            set { contaningClass = value; }
        }
        
        private List<VariableAbstraction> atfdVars;

        public List<VariableAbstraction> AtfdVars
        {
            get
            {
                if (this.atfdVars == null)
                    this.atfdVars = LoafAtfdVars();
                return atfdVars; 
            }
        }

        private List<VariableAbstraction> atfdProperties;

        public List<VariableAbstraction> AtfdProperties
        {
            get
            {
                if (this.atfdProperties == null)
                    this.atfdProperties = LoafAtfdProperties();
                return atfdProperties;
            }
        }

        private string ilMethodCode;

        public string ILMethodCode
        {
            get
            {
                if (String.IsNullOrEmpty(ilMethodCode))
                    ilMethodCode = GetILCode();
                return ilMethodCode; 
            }
        }

        public MethodAbstraction(CodeFunction2 methodObject, CodeClass2 containingClass, 
            List<VariableAbstraction> classInstanceVariables)
        {
            this.methodObject = methodObject;
            this.ContainingClass = containingClass;
            this.classInstanceVariables = classInstanceVariables;
            InitializeComponents();
            //GetClassVarsUsed();
        }

        private void InitializeComponents()
        {
            try
            {
                this.GetParameters();
            }
            catch (Exception ex)
            {                
            }
        }

        private void ParseMethodBody()
        {
            try
            {
                string anonymousClass = @"namespace anonymous { public class AnonymousClass { ";
                string code = this.methodObject.StartPoint.CreateEditPoint().GetText(this.methodObject.EndPoint);
                code = anonymousClass + code + @" } }";
                Lexer lex = new Lexer(new StreamReader(new MemoryStream(
                                                         Encoding.UTF8.GetBytes(code))));
                TokenCollection tc = lex.Lex();
                Parser p = new Parser(string.Empty);
                CompilationUnitNode n = p.Parse(tc, lex.StringLiterals);
                //this.localVariables = Visitor.localVariables;                
                n.AcceptVisitor(Visitor, null);
            }
            catch (Exception ex)
            {
                                
            }
        }

        private void GetParameters()
        {
            foreach (CodeParameter param in this.methodObject.Parameters)
                this.parameters.Add(new VariableAbstraction(param.Name, param.Type.AsString));            
        }

        private int CalculateCYCLO()
        {
            return this.Visitor.QtdFor + this.Visitor.QtdWhile + this.Visitor.QtdIf + this.Visitor.QtdCase + 1;
        }

        private int CalculateNOP()
        {
            return this.methodObject.Parameters.Count;
        }

        private int CalculateLOC()
        {
            return (this.methodObject.EndPoint.Line - this.methodObject.StartPoint.Line) + 1;
        }

        private int CalculateNOAV()
        {
            List<VariableAbstraction> classVarsUsed = this.GetClassVarsUsed();
            return classVarsUsed.Count + this.LocalVariables.Count + this.parameters.Count;
        }

        private int CalculateMAXNESTING()
        {
            return 0;
        }

        private string GetILCode()
        {
            System.Reflection.Assembly a;
            try
            {
                string path = GetAssemblyOutputPath.GetAssemblyPath(this.MethodObject.ProjectItem.ContainingProject);
                string asmName = path.Substring(path.LastIndexOf("\\") + 1);
                string path2 = SolutionBuilder.lastCompiledProj + "\\" + asmName;

                a = System.Reflection.Assembly.LoadFrom(path);
                Type type = a.GetType(this.ContainingClass.FullName);
                MethodInfo methodInfo = type.GetMethod(this.MethodObject.Name);
                MethodBodyReader mr = new MethodBodyReader(methodInfo);
                return mr.GetBodyCode();
            }
            catch (Exception)
            {
                //dispose Pointer
                a = null;
            }
            return "";

        }

        private List<VariableAbstraction> GetClassVarsUsed()
        {
            List<VariableAbstraction> classVarsUsed;
            classVarsUsed = GetVariablesUsedFromIL.GetUsedInstanceVars(this.ILMethodCode);
            return classVarsUsed;
        }

        private List<VariableAbstraction> LoafAtfdVars()
        {
            List<VariableAbstraction> atfdVars;
            atfdVars = GetVariablesUsedFromIL.GetAtfdVars(this.ILMethodCode, new List<string>()
            {
                this.ContainingClass.FullName
            });
            return atfdVars;
        }

        private List<VariableAbstraction> LoafAtfdProperties()
        {
            List<VariableAbstraction> atfdProperties;
            atfdProperties = GetVariablesUsedFromIL.GetAtfdProperties(this.ILMethodCode, new List<string>()
            {
                this.ContainingClass.FullName
            });
            return atfdProperties;
        }
    }
}
